//
//  RandomForest.cpp
//  RandomForest-AndreaBalboni
//
//  Created by Andrea Balboni on 07/04/12.
//  Copyright (c) 2012 Università degli Studi di Modena e Reggio Emilia. All rights reserved.
//

#include <map>
#include <algorithm>

#include "RandomForest.h"
#include <string>
#include <iostream>



/** 
 * Costruttore della classe random tree
 */
template <typename _T> RandomForest<_T>::RandomForest() : _randomTrees(0) {
	//cout << "Creating random forest " << endl;
}

//Creo la foresta leggendo da file
template <typename _T> void RandomForest<_T>::createForest(	std::string trainingDataPath,
                                                           unsigned int numTrees, 
                                                           unsigned int depth, unsigned int method) {
    
    this->_trainingData.readData(trainingDataPath);
    /* Alloco i Random Tree */
    this->_forestSize = numTrees;
    this->_randomTrees.resize(numTrees);
    
    /* Train di tutti gli alberi della foresta */
    for(unsigned int i=0 ; i<numTrees; i++)
        (_randomTrees.at(i)).trainTree(this->_trainingData, depth, method);
}

//Creo la foresta leggendo da file
template <typename _T> void RandomForest<_T>::createForest(	std::string trainingDataPath,
                                                           unsigned int numTrees, unsigned int method) {
    
    this->_trainingData.readData(trainingDataPath);
    /* Alloco i Random Tree */
    this->_forestSize = numTrees;
    this->_randomTrees.resize(numTrees);
    
    /* Train di tutti gli alberi della foresta */
    for(unsigned int i=0 ; i<numTrees; i++)
        (_randomTrees.at(i)).trainTree(this->_trainingData, method);
}

//Creo la foresta con reference a dataset precaricato
template <typename _T> void RandomForest<_T>::createForest(	TrainingData<_T> &trainingData,
                                                           unsigned int numTrees, 
                                                           unsigned int depth, unsigned int method, bool bagging, unsigned int numRecords) {
    
    /* Alloco i Random Tree */
    this->_forestSize = numTrees;
    this->_randomTrees.resize(numTrees);
    this->_trainingData=trainingData;
    
    std::vector<TrainingData<_T> > subDatasets(numTrees);
    if(bagging){
        subDatasets.clear();
        for(unsigned int i=0 ; i<numTrees; i++){
            
            TrainingData<_T> subData;
            subData.setFeatureSpaceDimension(trainingData.getFeatureSpaceDimension());
            for (unsigned int i=0; i<numRecords; ++i) {
                unsigned int r=(unsigned int)rand()%trainingData.size();
                subData.push_Record(trainingData.getRecord(r));
            }
            subDatasets.push_back(subData);
        }
    }
    /* Train di tutti gli alberi della foresta */
    for(unsigned int i=0; i<numTrees; i++){
        if(bagging){
            this->_trainingData=subDatasets.at(i);
        }
        else {
            this->_trainingData=trainingData;
        }
        //std::cout<<"Training tree # "<<i+1<<std::endl;
        (_randomTrees.at(i)).trainTree(this->_trainingData, depth, method);
        
    }
}

//Creo la foresta con reference a dataset precaricato
template <typename _T> void RandomForest<_T>::createForest(	TrainingData<_T> &trainingData,
                                                           unsigned int numTrees, unsigned int method, bool bagging, unsigned int numRecords) {
    
    /* Alloco i Random Tree */
    this->_forestSize = numTrees;
    this->_randomTrees.resize(numTrees);
    this->_trainingData=trainingData;
    
    std::vector<TrainingData<_T> > subDatasets(numTrees);
    if(bagging){
        subDatasets.clear();
        for(unsigned int i=0 ; i<numTrees; i++){
            
            TrainingData<_T> subData;
            subData.setFeatureSpaceDimension(trainingData.getFeatureSpaceDimension());
            for (unsigned int i=0; i<numRecords; ++i) {
                unsigned int r=(unsigned int)rand()%trainingData.size();
                subData.push_Record(trainingData.getRecord(r));
            }
            subDatasets.push_back(subData);
        }
    }
    /* Train di tutti gli alberi della foresta */
    for(unsigned int i=0; i<numTrees; i++){
        if(bagging){
            this->_trainingData=subDatasets.at(i);
        }
        else {
            this->_trainingData=trainingData;
        }
        //std::cout<<"Training tree # "<<i+1<<std::endl;
        (_randomTrees.at(i)).trainTree(this->_trainingData, method);
        
    }
    
}

/**
 * Classifica un record 
 */
template <typename _T> unsigned int RandomForest<_T>::classify(MyRecord<_T> &record) {
    
    std::map<unsigned int, unsigned int> classFreq;
    
	for(unsigned int i=0 ;i<this->_forestSize; i++) {
		unsigned int c = (this->_randomTrees.at(i)).classify(record);
        
		if(classFreq.find(c) != classFreq.end()) {
			classFreq[c] ++;
		} else {
			classFreq[c]= 1;
		}
	}
    unsigned int count=0,classe=0;
    for(std::map<unsigned int, unsigned int>::iterator it=classFreq.begin(); it != classFreq.end(); it++)
	{
        if(count < it->second)
		{
			classe = it->first;
			count = it->second;
		}
		
	}
    return classe;
}

template <typename _T> unsigned int RandomForest<_T>::classifyFisher(cv::Mat_<double> &record) {
    
    std::map<unsigned int, unsigned int> classFreq;
    
	for(unsigned int i=0 ;i<this->_forestSize; i++) {
		unsigned int c = (this->_randomTrees.at(i)).classifyFisher(record);
        
		if(classFreq.find(c) != classFreq.end()) {
			classFreq[c] ++;
		} else {
			classFreq[c]= 1;
		}
	}
    unsigned int count=0,classe=0;
    for(std::map<unsigned int, unsigned int>::iterator it=classFreq.begin(); it != classFreq.end(); it++)
	{
        if(count < it->second)
		{
			classe = it->first;
			count = it->second;
		}
		
	}
    return classe;
}



template <typename _T> RandomForest<_T>::~RandomForest() {
    //std::cout << "Destroying forest " << std::endl;
}

template class RandomForest<float>;
template class RandomForest<int>;
template class RandomForest<double>;
template class RandomForest<unsigned char>;